package com.xlh.virtualization.service.impl;

import com.google.common.collect.Lists;
import com.xlh.dto.course.ChapterBasicInfoDTO;
import com.xlh.dto.course.CourseBasicDTO;
import com.xlh.enums.course.ChapterEnum;
import com.xlh.enums.course.CourseStatusEnum;
import com.xlh.exception.common.GlobalException;
import com.xlh.exception.common.NotFoundException;
import com.xlh.service.course.ChapterService;
import com.xlh.service.course.CourseService;
import com.xlh.service.courseTest.QuestionRepositoryService;
import com.xlh.support.training.domain.Training;
import com.xlh.support.training.service.TrainingService;
import com.xlh.virtualization.constant.ContextTypeEnum;
import com.xlh.virtualization.dao.ContainerMapper;
import com.xlh.virtualization.dao.ImageMapper;
import com.xlh.virtualization.dao.helper.ContainerMapperHelper;
import com.xlh.virtualization.dao.helper.ImageMapperHelper;
import com.xlh.dokka.api.DokkaImageService;
import com.xlh.virtualization.domain.Container;
import com.xlh.virtualization.domain.Image;
import com.xlh.virtualization.po.ImageSavePO;
import com.xlh.virtualization.properties.ContainerProperties;
import com.xlh.virtualization.service.ConfigService;
import com.xlh.virtualization.service.ImageAllocationImageService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.List;
import java.util.Objects;
import java.util.UUID;

import static com.xlh.virtualization.constant.ContextTypeEnum.*;
import static com.xlh.virtualization.constant.ImageConstant.SYSTEM_PRESET_CODE;
import static com.xlh.virtualization.constant.ImageConstant.TEACHER_MADE_CODE;

/**
 * @author cheer
 */
@Service
public class ImageAllocationImageServiceImpl implements ImageAllocationImageService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private ConfigService configService;

    @Autowired
    private DokkaImageService dokkaImageService;

    @Autowired
    private ChapterService chapterService;

    @Autowired
    private QuestionRepositoryService questionRepositoryService;

    @Autowired
    private CourseService courseService;

    @Autowired
    private TrainingService trainingService;

    @Autowired
    private ContainerProperties containerProperties;

    @Autowired
    private ImageMapperHelper imageMapperHelper;

    @Autowired
    private ContainerMapperHelper containerMapperHelper;

    @Autowired
    private ImageMapper imageMapper;

    @Autowired
    private ContainerMapper containerMapper;

    @Override
    public Long saveImage(Long userId, Boolean isAdmin, ImageSavePO imageSavePO) {
        Container container = containerMapperHelper.getContainerById(imageSavePO.getContainerId());
        if (container == null) {
            throw new GlobalException("容器不存在");
        }
        Integer imageContextType = ContextTypeEnum.toImage(container.getContextType());
        if (imageContextType == null) {
            throw new GlobalException("容器类型无效");
        }
        Image basedImage = imageMapperHelper.getById(container.getImageId());
        if (basedImage == null) {
            throw new GlobalException("原始镜像不存在");
        }
        String imageName = StringUtils.trimToNull(imageSavePO.getImageName());
        if (imageMapperHelper.isExistsImageName(imageName)) {
            throw new GlobalException("镜像名称已存在");
        }
        long imageContextId = getImageContextId(container.getContextType(), container.getContextId());

        // 如果是课程镜像，验证镜像数量限制
        if (Objects.equals(imageContextType, TASK.getCode()) && !isAdmin) {
            int imageQuota = configService.getImageQuota();
            if (imageMapperHelper.countByContext(userId, imageContextType, imageContextId) >= imageQuota) {
                throw new GlobalException(String.format("每个教师每门课程保存镜像的数量不能超过 %s 个", imageQuota));
            }
        }

        // 构建镜像并推送到镜像仓库上
        String imageNameInDocker = UUID.randomUUID().toString();
        String imageNameWithDockerRegistry = containerProperties.getRegistry() + "/" + imageNameInDocker;
        if (!dokkaImageService.saveImage(container.getDokkaId(), imageNameWithDockerRegistry)) {
            throw new GlobalException("保存镜像异常");
        }

        boolean shared = isAdmin ? true : getShared(imageContextType, imageContextId);
        Image image = Image.builder()
                .imageName(imageName)
                .icon(imageSavePO.getIcon())
                .description(imageSavePO.getDescription())
                .imageNameInDocker(imageNameInDocker)
                .gpu(basedImage.getGpu())
                .imageSource(isAdmin ? SYSTEM_PRESET_CODE : TEACHER_MADE_CODE)
                .userId(userId)
                .contextType(imageContextType)
                .contextId(imageContextId)
                .shared(shared)
                .build();

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                imageMapper.insertSelective(image);
                Container updateContainer = Container.builder()
                        .id(container.getId())
                        .imageId(image.getId())
                        .build();
                containerMapper.updateByPrimaryKeySelective(updateContainer);
            }
        });

        return image.getId();
    }

    @Override
    public void coverImage(Long userId, Long imageId, ImageSavePO imageSavePO) {
        Image image = imageMapperHelper.getById(imageId);
        if (image == null) {
            throw new GlobalException("镜像不存在");
        }
        Container container = containerMapperHelper.getContainerById(imageSavePO.getContainerId());
        if (container == null) {
            throw new GlobalException("容器不存在");
        }
        String imageName = StringUtils.trimToNull(imageSavePO.getImageName());
        if (!StringUtils.equals(imageName, image.getImageName()) && imageMapperHelper.isExistsImageName(imageName)) {
            throw new GlobalException("镜像名称已存在");
        }

        String imageNameInDocker = UUID.randomUUID().toString();
        String imageNameWithDockerRegistry = containerProperties.getRegistry() + "/" + imageNameInDocker;
        if (dokkaImageService.saveImage(container.getDokkaId(), imageNameWithDockerRegistry)) {
            Image updateImage = Image.builder()
                    .id(imageId)
                    .imageName(imageName)
                    .icon(imageSavePO.getIcon())
                    .description(imageSavePO.getDescription())
                    .imageNameInDocker(imageNameInDocker)
                    .build();
            imageMapper.updateByPrimaryKeySelective(updateImage);
        } else {
            throw new GlobalException("保存镜像异常");
        }
    }

    @Override
    public List<Image> listTeacherVisibleImages(Long userId, Boolean excludeGpu) {
        return imageMapperHelper.listTeacherVisibleImages(userId, excludeGpu);
    }

    @Override
    public List<Image> listAdminVisibleImages(Boolean excludeGpu) {
        return imageMapperHelper.listSharedImages(excludeGpu);
    }

    @Override
    public Boolean isImageOwner(Long imageId, Long userId) {
        Image image = imageMapperHelper.getById(imageId);
        return image != null && Objects.equals(image.getUserId(), userId);
    }

    private Long getImageContextId(Integer contextType, Long contextId) {
        if (Objects.equals(COURSE_IMAGE_ALLOCATION.getCode(), contextType)) {
            List<ChapterBasicInfoDTO> chapterBasicInfoList = chapterService.listChapter(null,
                    Lists.newArrayList(contextId), null, ChapterEnum.TASK);
            if (CollectionUtils.isEmpty(chapterBasicInfoList)) {
                throw new GlobalException("保存镜像异常：找不到对应的课程");
            }
            return chapterBasicInfoList.get(0).getCourseId();

        } else if (Objects.equals(TRAINING_IMAGE_ALLOCATION.getCode(), contextType)) {
            return contextId;

        } else if (Objects.equals(QUESTION_IMAGE_ALLOCATION.getCode(), contextType)) {
            Long courseId = questionRepositoryService.getCourseId(contextId);
            if (courseId == null) {
                throw new GlobalException("保存镜像异常：找不到对应的课程");
            }
            return courseId;
        }
        throw new GlobalException("容器类型无效");
    }

    private Boolean getShared(Integer imageContextType, Long imageContextId) {
        if (Objects.equals(TASK.getCode(), imageContextType)) {
            try {
                CourseBasicDTO courseBasic = courseService.getCourse(imageContextId);
                return Objects.equals(CourseStatusEnum.SHARED.getCode(), courseBasic.getStatus());
            } catch (NotFoundException e) {
                throw new GlobalException("保存镜像异常：找不到对应的课程");
            }
        } else if (Objects.equals(TRAINING.getCode(), imageContextType)) {
            Training training = trainingService.getTraining(imageContextId);
            if (training == null) {
                throw new GlobalException("保存镜像异常：找不到对应的环境实训");
            }
            return training.getPublished();
        }
        throw new GlobalException("容器类型无效");
    }
}
